/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[欢迎加盟知启蒙，一起邂逅框架梦]
 * 
 * https://www.zhiqim.com/gitcan/zhiqim/zhiqim_account.htm
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zhiqim.account.presenter;

import java.sql.Timestamp;

import org.zhiqim.account.ZacConstants;
import org.zhiqim.account.ZacPassworder;
import org.zhiqim.account.ZacSessionUser;
import org.zhiqim.account.cache.SensitiveWordCache;
import org.zhiqim.account.dbo.user.UserAccount;
import org.zhiqim.account.dbo.user.UserInfo;
import org.zhiqim.account.dbo.user.UserVerification;
import org.zhiqim.httpd.HttpRequest;
import org.zhiqim.httpd.context.ZmlContextConstants;
import org.zhiqim.httpd.validate.ones.IsLen;
import org.zhiqim.httpd.validate.ones.IsNumericLen;
import org.zhiqim.httpd.validate.onex.IsEmail;
import org.zhiqim.httpd.validate.onex.IsMobile11;
import org.zhiqim.httpd.validate.onex.IsRegexp;
import org.zhiqim.kernel.Global;
import org.zhiqim.kernel.annotation.AnAlias;
import org.zhiqim.kernel.annotation.AnTransaction;
import org.zhiqim.kernel.constants.RegexpConstants;
import org.zhiqim.kernel.util.DateTimes;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Ids;
import org.zhiqim.kernel.util.Images;
import org.zhiqim.kernel.util.Randoms;
import org.zhiqim.kernel.util.Sqls;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;
import org.zhiqim.kernel.util.codes.Base64;
import org.zhiqim.kernel.util.consts.Int;
import org.zhiqim.mail.MailSender;
import org.zhiqim.orm.ZTable;
import org.zhiqim.orm.dbo.Selector;
import org.zhiqim.orm.dbo.Updater;
import org.zhiqim.sms.SMSSender;

/**
 * 用户属性处理器
 *
 * @version v1.0.0 @author zhichenggang 2017-7-2 新建与整理
 */
@AnAlias("UserProfilePresenter")
public class UserProfilePresenter implements ZmlContextConstants, ZacConstants
{
    /** 修改用户昵称 */
    public static void doRenameNick(HttpRequest request) throws Exception
    {
        request.addValidate(new IsLen("userNick", "请输入1-32个字符地昵称", 1, 32));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        String userNick = request.getParameter("userNick");
        if (Global.get(SensitiveWordCache.class).isSensitive(userNick) || Global.get(SensitiveWordCache.class).isSimilar(userNick))
        {
            request.setResponseError("昵称包含敏感词，请重新输入");
            return;
        }
        
        if(Global.get(ZTable.class).count(UserInfo.class, new Selector("userNick", userNick)) > 0)
        {
            request.setResponseError("昵称已被使用，请重新输入");
            return;
        }
        
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        long userId = sessionUser.getUserId();
        
        Updater updater = new Updater();
        updater.addField("userNick", userNick);
        updater.addField("userModified", Sqls.nowTimestamp());
        updater.addMust("userId", userId);
        
        Global.get(ZTable.class).update(UserInfo.class, updater);
        sessionUser.setUser(Global.get(ZTable.class).item(UserInfo.class, userId));//更新缓存
    }
    
    /**修改密码*/
    public static void doModifyPass(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsRegexp("newPass", "新密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        request.addValidate(new IsRegexp("newPass2", "确认密码要求6-16位的大小写字母和.@_#$符号", RegexpConstants.USER_PASS));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查验证码
        String oldPass = request.getParameter("oldPass");
        String newPass = request.getParameter("newPass");
        String newPass2 = request.getParameter("newPass2");
        
        if(!newPass.equals(newPass2))
        {
            request.setResponseError("确认密码和新密码不一致，请重新输入");
            return;
        }
        
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        UserInfo user = sessionUser.getUser();
        boolean oldPassExist = Validates.isNotEmpty(user.getUserPass());//旧密码是否为空
        
        if(oldPassExist && newPass.equals(oldPass))
        {
            request.setResponseError("新密码和旧密码一致，无须修改");
            return;
        }
        
        long userId = sessionUser.getUserId();
        
        //3.验证原密码
        if(oldPassExist)
        {
            String oldUserPassSalt = user.getUserPassSalt();
            
            String userOldPass = ZacPassworder.encode(userId, oldPass, oldUserPassSalt);
            if(!user.getUserPass().equalsIgnoreCase(userOldPass))
            {
                request.setResponseError("原密码不正确，请重新输入");
                return;
            }
        }
        
        //4.更新用户密码
        String userPassSalt = Randoms.lettersDigitsSecure(64);
        String userNewPass = ZacPassworder.encode(userId, newPass, userPassSalt);
        Timestamp userModified = Sqls.nowTimestamp();
        
        Updater updater = new Updater()
            .addMust("userId", userId)
            .addField("userPass", userNewPass)
            .addField("userPassSalt", userPassSalt)
            .addField("userModified", userModified);
        
        Global.get(ZTable.class).update(UserInfo.class, updater);
        
        //修改会话中的数据
        user.setUserPass(userNewPass);
        user.setUserPassSalt(userPassSalt);
        user.setUserModified(userModified);
    }
    
    /**修改手机号*/
    @AnTransaction
    public static void doModifyMobile(HttpRequest request) throws Exception
    {
        request.addValidate(new IsMobile11("userAccount", "请输入正确格式的手机号"));
        request.addValidate(new IsNumericLen("validCode", "请输入正确的验证码", 6, 6));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        String userAccount = request.getParameter("userAccount");
        String validCode = request.getParameter("validCode");
        
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        long userId = sessionUser.getUserId();
        
        //验证验证码
        String result = UserPassportPresenter.chkValidCode(userAccount, validCode, 2);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        if(userAccount.equals(sessionUser.getUserMobile()))
        {
            request.setResponseError("手机号未改变，不需要修改");
            return;
        }
        
        String accountMobile = sessionUser.getAccountMobile();
        if (Validates.isMobile11(accountMobile))
        {//旧手机号是登录名的
            if (!Validates.isEmail(sessionUser.getAccountEmail()))
            {
                request.setResponseError("必须要有一个可以作为登录名的邮箱，才可以修改手机号");
                return;
            }
        }

        if (Global.get(ZTable.class).count(UserAccount.class, userAccount) == 0)
        {//新手机号没有被作为登录名的，1)增加到账号中，2)增加到信息中
            UserAccount account = new UserAccount();
            account.setUserId(userId);
            account.setUserAccount(userAccount);
            account.setAccountType(ACCOUNT_TYPE_MOBILE);
            Global.get(ZTable.class).insert(account);
            
            Global.get(ZTable.class).update(UserInfo.class, new Updater()
                                                                .addMust("userId", userId)
                                                                .addField("userMobile", userAccount)
                                                                .addField("userModified", Sqls.nowTimestamp()));
        }
        else
        {//新手机号被当用户名的，判断最多5个，只绑定
        
            if (Global.get(ZTable.class).count(UserInfo.class, new Selector("userMobile", userAccount)) >= 5)
            {//手机号最多绑定5个账号
                request.setResponseError("一个手机号最多只能绑定5个账号");
                return;
            }
        
            Global.get(ZTable.class).update(UserInfo.class, new Updater()
                                                                .addMust("userId", userId)
                                                                .addField("userMobile", userAccount)
                                                                .addField("userModified", Sqls.nowTimestamp()));
        }
        
        //释放旧手机
        if (Validates.isNotEmptyBlank(accountMobile))
        {
            Global.get(ZTable.class).delete(UserAccount.class, accountMobile);
        }
        
        //最新更新会话
        sessionUser.setUser(Global.get(ZTable.class).item(UserInfo.class, userId))
                   .setAccountList(Global.get(ZTable.class).list(UserAccount.class, new Selector("userId", userId)));
    }
    
    /**修改邮箱*/
    @AnTransaction
    public static void doModifyEmail(HttpRequest request) throws Exception
    {
        request.addValidate(new IsEmail("userAccount", "请输入正确的电子邮箱"));
        request.addValidate(new IsNumericLen("validCode", "请输入正确的验证码", 6, 6));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        String userAccount = request.getParameter("userAccount");
        String validCode = request.getParameter("validCode");
        
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        long userId = sessionUser.getUserId();
        
        //验证验证码
        String result = UserPassportPresenter.chkValidCode(userAccount, validCode, 3);
        if (result != null)
        {
            request.setResponseError(result);
            return;
        }
        
        if(userAccount.equals(sessionUser.getUserEmail()))
        {
            request.setResponseError("电子邮箱未改变，不需要修改");
            return;
        }
        
        String accountEmail = sessionUser.getAccountEmail();
        if (Validates.isEmail(accountEmail))
        {//旧邮箱是登录名的
            if (!Validates.isMobile11(sessionUser.getAccountMobile()))
            {
                request.setResponseError("必须要有一个可以作为登录名的手机号，才可以修改邮箱");
                return;
            }
        }

        if (Global.get(ZTable.class).count(UserAccount.class, userAccount) == 0)
        {//新邮箱没有被作为登录名的，1)增加到账号中，2)增加到信息中
            UserAccount account = new UserAccount();
            account.setUserId(userId);
            account.setUserAccount(userAccount);
            account.setAccountType(ACCOUNT_TYPE_EMAIL);
            Global.get(ZTable.class).insert(account);
            
            Global.get(ZTable.class).update(UserInfo.class, new Updater()
                                                                .addMust("userId", userId)
                                                                .addField("userEmail", userAccount)
                                                                .addField("userModified", Sqls.nowTimestamp()));
        }
        else
        {//新邮箱号被当用户名的，判断最多5个，只绑定
            if (Global.get(ZTable.class).count(UserInfo.class, new Selector("userEmail", userAccount)) >= 5)
            {//手机号最多绑定5个账号
                request.setResponseError("一个邮箱最多只能绑定5个账号");
                return;
            }
        
            Global.get(ZTable.class).update(UserInfo.class, new Updater()
                                                                .addMust("userId", userId)
                                                                .addField("userEmail", userAccount)
                                                                .addField("userModified", Sqls.nowTimestamp()));
        }
        
        //释放旧邮箱
        if (Validates.isNotEmptyBlank(accountEmail))
        {
            Global.get(ZTable.class).delete(UserAccount.class, accountEmail);
        }
        
        //最新更新会话
        sessionUser.setUser(Global.get(ZTable.class).item(UserInfo.class, userId))
                   .setAccountList(Global.get(ZTable.class).list(UserAccount.class, new Selector("userId", userId)));
    }
    
    /**
     * 修改头像
     * 
     * @param request       请求
     * @param data          图片数据
     * @throws Exception    异常
     */
    public static void doModifyAvatar(HttpRequest request, String data) throws Exception
    {
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        if(Validates.isEmpty(data))
        {
            request.setResponseError("图片数据不能为空");
            return;
        }
        
        try
        {
            int idx = data.indexOf(",");
            byte[] imageData = Base64.decode(data.substring(idx+1));
            
            int[] nums = {50, 100, 150};
            
            String rootDir = request.getContext().getAttributeString(_SERV_UPLOAD_ROOT_DIR_);
            for(int num : nums)
            {
                String fileName = sessionUser.getUserId() + "_" + num + ".png";
                String savePath = Strings.addEndsWith(rootDir, "/") + fileName;
                
                byte[] iData = Images.resizePNG(imageData, num, num);
                Files.write(savePath, iData);
            }
            
            Updater updater = new Updater();
            updater.addField("userAvatar50", _PATH_UPLOAD_PREFIX_ + sessionUser.getUserId() + "_50.png");
            updater.addField("userAvatar100", _PATH_UPLOAD_PREFIX_ + sessionUser.getUserId() + "_100.png");
            updater.addField("userAvatar150", _PATH_UPLOAD_PREFIX_ + sessionUser.getUserId() + "_150.png");
            updater.addMust("userId", sessionUser.getUserId());
            
            Global.get(ZTable.class).update(UserInfo.class, updater);
            sessionUser.setUser(Global.get(ZTable.class).item(UserInfo.class, sessionUser.getUserId()));//更新缓存
        }
        catch (Exception e)
        {
            request.setResponseError("图片存储异常，请重新操作");
        }
    }
    

    /** 发送修改手机号验证码 */
    public static void doSendVerifyCodeByModifyMobile(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsMobile11("userAccount", "请输入邮箱正确格式的手机号"));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查账号
        String userAccount = request.getParameter("userAccount");
        
        //3.发送短信验证码
        int verificationType = 2; //修改手机号
        String verificationCode = Randoms.digits(6);
        Int ret = Global.getService(SMSSender.class).sendVerificationCode(userAccount, verificationCode);
        if(ret.value() != 0)
        {
            request.setResponseError(ret.desc());
            return;
        }
        
        //4.保存验证码到数据库
        UserVerification verification = new UserVerification();
        verification.setVerificationId(Ids.longId19());
        verification.setUserAccount(userAccount);
        verification.setVerificationCode(verificationCode);
        verification.setVerificationStatus(0);
        verification.setVerificationType(verificationType);
        verification.setVerificationDate(DateTimes.getDateString());
        verification.setVerificationEndTime(DateTimes.getNextDateTimeStringBySecond(60*20));//20分钟有效
        Global.get(ZTable.class).insert(verification);
        
        request.setResponseResult("验证码已发送至" + userAccount + "，请验证");
    }
    
    /** 发送修改邮箱验证码 */
    public static void doSendVerifyCodeByModifyEmail(HttpRequest request) throws Exception
    {
        //1.检查参数
        request.addValidate(new IsEmail("userAccount", "请输入正确格式的邮箱"));
        if (!request.chkValidate())
        {
            request.setResponseError(request.getAlertMsg());
            return;
        }
        
        //2.检查账号
        String userAccount = request.getParameter("userAccount");
        int verificationType = 3; //修改邮箱
        
        
        //3.发送邮箱验证码
        String verificationCode = Randoms.digits(6);
        String subject = "知启蒙邮箱注册验证码";
        String content = "【知启蒙】您的注册验证码是：%s，验证码十分钟内有效。";
        content = Strings.format(content, verificationCode);
        
        String ret = Global.getService(MailSender.class).sendTextMail(userAccount, subject, content);
        if(Validates.isNotEmpty(ret))
        {
            request.setResponseError("找回密码邮件发送失败，请重新填写");
            return;
        }
        
        //4.保存验证码到数据库
        UserVerification verification = new UserVerification();
        verification.setVerificationId(Ids.longId19());
        verification.setUserAccount(userAccount);
        verification.setVerificationCode(verificationCode);
        verification.setVerificationStatus(0);
        verification.setVerificationType(verificationType);
        verification.setVerificationDate(DateTimes.getDateString());
        verification.setVerificationEndTime(DateTimes.getNextDateTimeStringBySecond(60*20));//20分钟有效
        Global.get(ZTable.class).insert(verification);
        
        request.setResponseResult("验证码已发送至" + userAccount + "，请验证");
    }
    
    /**
     * 保存默认支付方式
     * @param request
     * @throws Exception
     */
    public static void doSavePayment(HttpRequest request)throws Exception
    {
        String paymentCode = request.getParameter("paymentCode");
        ZacSessionUser sessionUser = request.getSessionUser(ZacSessionUser.class);
        long userId = sessionUser.getUser().getUserId();
        
        Updater updater = new Updater();
        updater.addMust("userId", userId);
        updater.addField("userPayment", paymentCode);
        Global.get(ZTable.class).update(UserInfo.class, updater);
        
        sessionUser.setUser(Global.get(ZTable.class).item(UserInfo.class, userId));//更新缓存
    }
}
